package cc.ifok.rpc;

import cc.ifok.kit.Message;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.framing.CloseFrame;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public final class RPCClient {
    private static final Logger LOG = LoggerFactory.getLogger(RPCClient.class);

    private final AtomicReference<WebSocketClient> client = new AtomicReference<>();

    public RPCClient(String uri){daemonWebsocket(uri);startInDaemon();}

    public boolean send(String message) {
        if (opened()) {
            try {client.get().send(message);return true;
            }catch(Throwable e){/*ignore*/return false;}
        }   return false;
    }

    private boolean opened() {
        return client.get().isOpen();
    }
    private void daemonWebsocket(String uri) {
        Optional.ofNullable(client.get()).ifPresent(socket -> {
            try{socket.close(CloseFrame.UNEXPECTED_CONDITION);}catch(Throwable e){/*ignore*/}
        });
        client.set(new WebSocketClient(URI.create(uri)) {
            @Override
            public void onOpen(ServerHandshake handshake) {
                LOG.info("Connected to server success! URI=" + uri);
            }
            @Override
            public void onClose(int code, String reason, boolean remote) {
                LOG.debug("Connection closed CODE=" + code + ", REASON=" + reason + ", REMOTE=" + remote);
            }
            @Override
            public void onError(Exception e) {
                LOG.error("Websocket ERR=" + e.getMessage() + ", URI=" + uri);
                this.close(CloseFrame.UNEXPECTED_CONDITION);
            }
            @Override
            public void onMessage(String sss) {
                try{Message.RPS.put(new Message(sss));}catch(Throwable e){/*ignore*/}
            }
        });
    }
    private boolean checkUriLikOK(String host, int port) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(host, port), 2222);
            return true;
        } catch (Throwable e) {e.printStackTrace();return false;}
    }
    private static final ScheduledExecutorService TIMER = Executors.newSingleThreadScheduledExecutor();
    private void startInDaemon() {
        TIMER.scheduleAtFixedRate(() -> {
            if (!opened() && checkUriLikOK(client.get().getURI().getHost(), client.get().getURI().getPort())) {
                try {
                    client.get().connectBlocking(8192, TimeUnit.MILLISECONDS);
                } catch (Throwable e) {
                    daemonWebsocket(client.get().getURI().toString());
                    try{client.get().connectBlocking(8192, TimeUnit.MILLISECONDS);}catch(Throwable ex){/*ignore*/}
                }
            }
        }, 128, 5120, TimeUnit.MILLISECONDS);
    }
}
